package com.space.rabbitmq.config;

import com.space.rabbitmq.mqcallback.MsgSendConfirmCallBack;
import com.space.rabbitmq.mqcallback.MsgSendReturnCallback;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * RabbitMq配置
 *
 * @author zhanglei
 */
@Configuration
public class RabbitMqConfig {

    /** 消息交换机的名字 1 */
    public static final String EXCHANGE_ONE = "first-exchange";
    /** 消息交换机的名字 2 */
    public static final String EXCHANGE_TWO = "second-exchange";
    /** 死信消息交换机的名字 1 */
    public static final String EXCHANGE_ONE_DEAD = "first-dead-exchange";
    /** 延迟时间，单位毫秒 */
    public static final long DELAY_TIME = 10000;



    /** 对列1 */
    public static final String QUEUE_NAME1 = "first-queue";
    /** 对列2 */
    public static final String QUEUE_NAME2 = "second-queue";
    /** 对列3 */
    public static final String QUEUE_NAME3 = "third-queue";
    /** 对列4 */
    public static final String QUEUE_NAME4 = "fourth-queue";
    /** 对列5 */
    public static final String QUEUE_NAME5 = "fifth-queue";
    /** 对列6 */
    public static final String QUEUE_NAME6 = "sixth-queue";
    /** 死信对列1 */
    public static final String QUEUE_NAME_DEAD1 = "first-dead-queue";
    /** 延迟对列1 */
    public static final String QUEUE_NAME_DELAY1 = "first-delay-queue";



    /** 队列key1*/
    public static final String ROUTINGKEY1 = "first-queue_key";
    /** 队列key2*/
    public static final String ROUTINGKEY2 = "second-queue_key";
    /** 队列key3*/
    public static final String ROUTINGKEY3 = "third-queue_key";
    /** 队列key4*/
    public static final String ROUTINGKEY4 = "fourth-queue_key";
    /** 队列key5*/
    public static final String ROUTINGKEY5 = "fifth-queue_key";
    /** 队列key6*/
    public static final String ROUTINGKEY6 = "sixth-queue_key";
    /** 死信队列key1*/
    public static final String ROUTINGKEY_DEAD1 = "first-queue_dead_key";
    /** 延迟队列key1*/
    public static final String ROUTINGKEY_DELAY1 = "first-queue_delay_key";



    /** 连接工厂 */
    @Autowired
    private ConnectionFactory connectionFactory;
    /** 队列配置文件 */
    @Autowired
    private QueueConfig queueConfig;
    /** 交换机配置文件 */
    @Autowired
    private ExchangeConfig exchangeConfig;



    /** 将消息队列1和交换机进行绑定,指定队列key1 */
    @Bean
    public Binding bindingOne() {
        return BindingBuilder.bind(queueConfig.firstQueue()).to(exchangeConfig.directExchangeOne()).with(RabbitMqConfig.ROUTINGKEY1);
    }
    /** 将消息队列2和交换机进行绑定,指定队列key2 */
    @Bean
    public Binding bindingTwo() {
        return BindingBuilder.bind(queueConfig.secondQueue()).to(exchangeConfig.directExchangeOne()).with(RabbitMqConfig.ROUTINGKEY2);
    }
    /** 将消息队列3和交换机进行绑定,指定队列key3 */
    @Bean
    public Binding bindingThree() {
        return BindingBuilder.bind(queueConfig.thirdQueue()).to(exchangeConfig.directExchangeOne()).with(RabbitMqConfig.ROUTINGKEY3);
    }
    /** 将消息队列4和交换机进行绑定,指定队列key4 */
    @Bean
    public Binding bindingFour() {
        return BindingBuilder.bind(queueConfig.fourthQueue()).to(exchangeConfig.directExchangeTwo()).with(RabbitMqConfig.ROUTINGKEY4);
    }
    /** 将消息队列5和交换机进行绑定,指定队列key5 */
    @Bean
    public Binding bindingFive() {
        return BindingBuilder.bind(queueConfig.fifthQueue()).to(exchangeConfig.directExchangeTwo()).with(RabbitMqConfig.ROUTINGKEY5);
    }
    /** 将消息队列6和交换机进行绑定,指定队列key6 */
    @Bean
    public Binding bindingSix() {
        return BindingBuilder.bind(queueConfig.sixthQueue()).to(exchangeConfig.directExchangeTwo()).with(RabbitMqConfig.ROUTINGKEY6);
    }
    /** 将死信队列1和死信交换机进行绑定,指定死信队列key1 */
    @Bean
    public Binding bindingDeadOne() {
        return BindingBuilder.bind(queueConfig.firstDeadQueue()).to(exchangeConfig.directExchangeDeadOne()).with(RabbitMqConfig.ROUTINGKEY_DEAD1);
    }
    /** 将延迟队列1和延迟交换机1进行绑定,指定延迟队列key1 */
    @Bean
    public Binding bindingDelayOne() {
        return BindingBuilder.bind(queueConfig.firstDelayQueue()).to(exchangeConfig.directExchangeDeadOne()).with(RabbitMqConfig.ROUTINGKEY_DELAY1);
    }



    /**
     * 自定义rabbit template用于数据的接收和发送
     * 可以设置消息确认机制和回调
     * @return
     */
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        // template.setMessageConverter(); 可以自定义消息转换器  默认使用的JDK的，所以消息对象需要实现Serializable

        /*
            若使用confirm-callback或return-callback，
            必须要配置publisherConfirms或publisherReturns为true
            每个rabbitTemplate只能有一个confirm-callback和return-callback
         */
        template.setConfirmCallback(msgSendConfirmCallBack());

        /*
            使用return-callback时必须设置mandatory为true，或者在配置中设置mandatory-expression的值为true，
            可针对每次请求的消息去确定’mandatory’的boolean值，
            只能在提供’return -callback’时使用，与mandatory互斥
         */
        template.setReturnCallback(msgSendReturnCallback());
        template.setMandatory(true);
        return template;
    }

    /*
        关于 msgSendConfirmCallBack 和 msgSendReturnCallback 的回调说明：
        1.如果消息没有到exchange,则confirm回调,ack=false
        2.如果消息到达exchange,则confirm回调,ack=true
        3.exchange到queue成功,则不回调return
        4.exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
     */

    /**
     * 消息确认机制
     * Confirms给客户端一种轻量级的方式，能够跟踪哪些消息被broker处理，
     * 哪些可能因为broker宕掉或者网络失败的情况而重新发布。
     * 确认并且保证消息被送达，提供了两种方式：发布确认和事务。(两者不可同时使用)
     * 在channel为事务时，不可引入确认模式；同样channel为确认模式下，不可使用事务。
     * @return
     */
    @Bean
    public MsgSendConfirmCallBack msgSendConfirmCallBack() {
        return new MsgSendConfirmCallBack();
    }

    @Bean
    public MsgSendReturnCallback msgSendReturnCallback() {
        return new MsgSendReturnCallback();
    }
}
